package com.crazyteam.datasource.factory;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.FilterManager;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.util.ClassUtils;

import javax.sql.DataSource;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * @author guoyang
 * @Date 2018-01-09
 * @Desc 数据源装配工厂
 */
public class DataSourceFactory extends AbstractFactoryBean<DataSource> {

    private Map<String, Object> prop;
    Class<? extends DataSource> datasourceType;

    public DataSourceFactory(Class<? extends DataSource> datasourceType, Map<String, Object> prop){
        this.datasourceType = datasourceType;
        this.prop = prop;
    }

    @Override
    public Class<?> getObjectType() {
        return datasourceType;
    }


    @Override
    protected DataSource createInstance() throws Exception {
        if (datasourceType.isAssignableFrom(DruidDataSource.class) ){
            DruidDataSource dataSource = (DruidDataSource)DruidDataSourceFactory.createDataSource(prop);
            if (prop.containsKey("filters")){
                String filters = (String) prop.get("filters");
                if (StringUtils.isNotEmpty(filters) && !filters.startsWith("!")){
                    Stream.of(filters.split(",")).forEach(filter -> {
                        if (StringUtils.isNotEmpty(filter.trim()) && prop.containsKey("filters." + filter.trim())){
                            String filterName = Optional.ofNullable(FilterManager.getFilter(filter.trim())).orElse(filter.trim());
                            dataSource.getProxyFilters().stream().filter(f -> f.getClass().getName().equalsIgnoreCase(filterName)).findFirst().ifPresent(destFilter -> {
                                BeanWrapper beanWrapper = new BeanWrapperImpl(destFilter);
                                String configs = (String)prop.get("filters." + filter.trim());
                                Stream.of(configs.split(";")).forEach(config ->{
                                    if (!config.contains("=") || config.split("=").length > 2){
                                        // do nothing
                                        throw new RuntimeException("DruidDataSource filters config:" + config + "配置无效");
                                    }
                                    String[] confs = config.split("=");
                                    if (confs[0].contains(".") || confs[0].split("\\.").length == 2){
                                        String[] props = confs[0].split("\\.");
                                        Object obj = beanWrapper.getPropertyValue(props[0]);
                                        if (obj == null){
                                            try {
                                                obj = beanWrapper.getPropertyType(props[0]).newInstance();
                                            } catch (Exception e) {
                                                throw new RuntimeException("DruidDataSource filters config:" + config + "配置异常", e);
                                            }
                                            beanWrapper.setPropertyValue(props[0], obj);
                                        }
                                        BeanWrapper attrWrapper = new BeanWrapperImpl(obj);
                                        attrWrapper.setPropertyValue(props[1], confs[1]);
                                    }else {
                                        if (beanWrapper.isWritableProperty(confs[0])){
                                            beanWrapper.setPropertyValue(confs[0], confs[1]);
                                        }
                                    }
                                });
                            });
                        }
                    });
                }
            }

            return dataSource;
        }else {
            DataSource dataSource = BeanUtils.instantiateClass(datasourceType);
            BeanWrapper beanWrapper = new BeanWrapperImpl(dataSource);
            prop.keySet().stream().filter((key) -> beanWrapper.isWritableProperty(key)).forEach((key) ->{
                beanWrapper.setPropertyValue(key, prop.get(key));
            });
            return dataSource;
        }
    }

}
